<?php
namespace app\common\business;

use app\common\lib\Redis;
use app\common\lib\ZhyxMongo;
use catcher\exceptions\FailedException;
use app\common\model\PatientInfo as PatientModel;
use app\common\model\OverallConfig as OverallModel;

class Follow
{
    /**
     * 筛选待随访患者
     * @return array
     */
    public function screenWaitFollowPatient(): array
    {
        $req = request()->param();

        $page = request()->param("page", 1, "int");
        $limit = request()->param("limit", 15, "int");
        $flag = request()->param("flag", 1, "int");
        $requestId = request()->param("request_id", "", "string");

        $userInfo = request()->user();

        if (!empty($requestId)) {
            $rnd = Redis::getInstance()->redis->get("advSearch:" . $req['request_id']);
            $rndArr = json_decode($rnd, true, 512, JSON_THROW_ON_ERROR);

            $cond = $this->preFollowPatientCondition($flag, $req['cycle']);
            $match = array_merge_recursive($rndArr, $cond);

        } else {
            //获取筛选条件
            $search = $this->getFollowScreenItem($req);

            //初始查询条件
            $cond = $this->preFollowPatientCondition($flag, $req['cycle']);

            //获取配置条件
            $match = array_merge_recursive($search, $cond);
        }

        //如果设置抽取比例
        if (isset($req['ratio']) && !empty($req['ratio'])) {
            //验证收取比例参数是不是大于0小于100的正整数
            if ((int)$req['ratio'] < 0 || (int)$req['ratio'] > 100) {
                throw new FailedException('抽取比例参数必须是大于0和小于100的数');
            }
            //计算抽取比例
            $totalPip = [['$match' => $match], ['$count' => 'number']];
            $total = (new PatientModel())->mongoAggregate($totalPip);
            $number = 0;
            foreach ($total as $k => $v) {
                $number = $v["number"];
            }
            $ratio = $number * ((int)$req['ratio'] / 100);
            $ratio = round($ratio);

            //全选条件
            $allSelectPip = [['$match' => $match], ['$sample' => ['size' => $ratio]], ['$project' => ['_id' => true, 'id' => '$_id']]];

            $pip = [['$match' => $match],  ['$sample' => ['size' => $ratio]], ['$project' => ['_id' => true, 'id' => '$_id', 'patientId' => '$patientId', 'name' => '$name', 'sex' => '$sex', 'birthdate' => '$birthdate', 'presentTelphone' => '$presentTelphone','medicalRecordsNo' => '$medicalRecordsNo', 'bz' => ['$ifNull' => [['$arrayElemAt' => ['$bz.bingzhongArr.bingzhong_name', 0]], '']],'diagnosis' => ['$ifNull' => ['$inList.diagnose', '']],'diseaseNo' => ['$ifNull' => ['$inList.diseaseNo', '']],'first_treat_datetime' => ['$ifNull' => ['$first_treat_datetime', '']],'first_treat_age' => ['$ifNull' => ['$first_treat_age', '']],'follow_time' => ['$ifNull' => ['$follow_node.follow_time', '']], 'taskname' => ['$ifNull' => ['$establish_followtask_node.taskname', '']], 'follow_result' => ['$ifNull' => ['$follow_node.resultName', '']]]], ['$sort' => ['_id' => -1]], ['$skip' => (($page - 1) * $limit)], ['$limit' => $limit]];

            $pipCount = [['$match' => $match], ['$sample' => ['size' => $ratio]],  ['$count' => 'total_count']];
        }else{
            //全选条件
            $allSelectPip = [['$match' => $match], ['$project' => ['_id' => true, 'id' => '$_id']]];

            $pip = [['$match' => $match], ['$project' => ['_id' => true, 'id' => '$_id', 'patientId' => '$patientId', 'name' => '$name', 'sex' => '$sex', 'birthdate' => '$birthdate', 'presentTelphone' => '$presentTelphone','medicalRecordsNo' => '$medicalRecordsNo', 'bz' => ['$ifNull' => [['$arrayElemAt' => ['$bz.bingzhongArr.bingzhong_name', 0]], '']],'diagnosis' => ['$ifNull' => ['$inList.diagnose', '']],'diseaseNo' => ['$ifNull' => ['$inList.diseaseNo', '']],'first_treat_datetime' => ['$ifNull' => ['$first_treat_datetime', '']],'first_treat_age' => ['$ifNull' => ['$first_treat_age', '']],'follow_time' => ['$ifNull' => ['$follow_node.follow_time', '']],'taskname' => ['$ifNull' => ['$establish_followtask_node.taskname', '']], 'follow_result' => ['$ifNull' => ['$follow_node.resultName', '']]]], ['$sort' => ['_id' => -1]], ['$skip' => (($page - 1) * $limit)], ['$limit' => $limit]];

            $pipCount = [['$match' => $match], ['$count' => 'total_count']];
        }

        //聚合总数
        $total = (new PatientModel())->mongoAggregate($pipCount);

        $totalCount = 0;
        foreach ($total as $key => $val) {
            $totalCount = $val["total_count"];
        }

        //聚合数据
        $data = (new PatientModel())->mongoAggregate($pip);

        $ret = [];
        foreach ($data as $key => $val) {
            $val['id'] = (string)$val['id'];
            $ret[] = $val;
        }

        //聚合全选患者id存入redis
        if ($page == 1) {
            $all_data = (new PatientModel())->mongoAggregate($allSelectPip);
            $pids = [];
            foreach ($all_data as $key => $val) {
                $pids[] = (string)$val['id'];
            }
            Redis::getInstance()->redis->setex('follow_select_all:' . $userInfo['doctor_code'], 300, json_encode($pids));
        }

        return [
            "count" => $totalCount,
            "current" => $page,
            "limit" => 15,
            "data" => $ret
        ];
    }

    /**
     * 获取随访全局配置
     * @return array
     */
    protected function getFollowOverall(): array
    {
        //全局随访筛选设置是否开启
        $overall = (new OverallModel())->getConditionOne(['flag' => 'sfsx'], 'status');

        $match = [];
        if ($overall['status'] === true){
            //过滤死亡的患者
            $match['liveStatus'] = '1';
            //过滤联系方式无效的患者
            $match['relationInfo.is_valid'] = ['$ne' => '2'];
            $match['presentTelphone'] = [
                '$exists' => true,
                '$ne' => ""
            ];
        }
        return $match;
    }

    /**
     * 获取随访患者筛选项
     * @param array $search
     * @return array
     */
    public function getFollowScreenItem(array $search=[]): array
    {
        //筛选参数
        $searchField = config('dictionary.follow_stati_screen');
        $medical = [];
        $match = [];
        foreach ($search as $key => $val) {
            if (!empty($searchField[$key]) && $key == $searchField[$key]) {
                if (!empty($val) && (is_string($val) || is_int($val) || is_array($val) && !empty(array_filter($val)))) {
                    if (is_string($val)) {
                        $val = trim($val);
                    }
                    switch ($key){
                        case 'medicalRecordsNo':
                            preg_match_all("/[A-Z]?\d+/", $val,$result);
                            if(!empty($result)){
                                $medical = $result[0];
                            }
                            //限制病案号个数不能大于5000
                            if (count($medical) > 5000) {
                                throw new FailedException("病案号个数不能大于5000");
                            }
                            $match['medicalRecordsNo'] = ['$in' => $medical];
                            break;
                        case 'startage':
                            $match['$and'][]['first_treat_age'] = ['$gte' => (string)$val];

                            //$match['first_treat_age'] = ['$gte' => (string)$val];
                            break;
                        case 'endage':
                            $match['$and'][]['first_treat_age'] = ['$lte' => (string)$val];
                            //$match['first_treat_age'] = ['$lte' => (string)$val];
                            break;
                        case 'sex':
                            $sex = config('dictionary.sex');
                            $match[$key] = $sex[$val];
                            break;
                        case 'diagnosis':
                            $match['inList.diagnose'] = ['$regex' => '^'.$val];
                            break;
                        case 'diseaseNo':
                            $match['inList.diseaseNo'] = $val;
                            break;
                        case 'diseaseType':
                            //获取病种编码
                            $match['bz.bingzhongArr.bingzhong_id']=['$in'=>$val];
                            break;
                        case 'mzDate':
                            if (isset($val[0]) && !empty($val[0])){
                                $match['outList.visitTime']['$gte'] = $val[0];
                            }
                            if (isset($val[1]) && !empty($val[1])){
                                $match['outList.visitTime']['$lte'] = $val[1];
                            }
                        break;
                        case 'inDate':
                            if (isset($val[0]) && !empty($val[0])){
                                $match['calc_date_node.last_in_hospital_date']['$gte'] =  $val[0];
                            }
                            if (isset($val[1]) && !empty($val[1])){
                                $match['calc_date_node.last_in_hospital_date']['$lte'] = $val[1];
                            }
                            break;
                        case 'outDate':
                            if (isset($val[0]) && !empty($val[0])){
                                $match['calc_date_node.last_out_hospital_date']['$gte'] = $val[0];
                            }
                            if (isset($val[1]) && !empty($val[1])){
                                $match['calc_date_node.last_out_hospital_date']['$lte'] = $val[1];
                            }
                            break;
                        case 'inAllDate':
                            if (isset($val[0]) && !empty($val[0])){
                                $match['calc_date_node.inhospitalDateArr']['$gte'] =  $val[0];
                            }
                            if (isset($val[1]) && !empty($val[1])){
                                $match['calc_date_node.inhospitalDateArr']['$lte'] = $val[1];
                            }
                            break;
                        case 'outAllDate':
                            if (isset($val[0]) && !empty($val[0])){
                                $match['calc_date_node.outhospitalDateArr']['$gte'] = $val[0];
                            }
                            if (isset($val[1]) && !empty($val[1])){
                                $match['calc_date_node.outhospitalDateArr']['$lte'] = $val[1];
                            }
                            break;
                        default:
                            break;
                    }
                }
            }
        }
        return $match;
    }

    /**
     * 预随访患者统计表
     * @return array
     */
    public function preFollowPatientStatistics($req): array
    {
        if (!empty($req['request_id'])) {
            $rnd = Redis::getInstance()->redis->get("advSearch:" . $req['request_id']);
            $rndArr = json_decode($rnd, true, 512, JSON_THROW_ON_ERROR);
            $totalMatch = $this->getFollowScreenItem($req);
            $totalMatch = array_merge_recursive($rndArr, $totalMatch);
        }else{
            $totalMatch = $this->getFollowScreenItem($req);
        }

        $match = [['$match' => $totalMatch], ['$count' => 'total_count']];

        //聚合总数
        $total = (new PatientModel())->mongoAggregate($match);

        $totalCount = 0;
        foreach ($total as $key => $val) {
            $totalCount = $val["total_count"];
        }

        $pending = $this->preFollowPatientAggregate($req);

        $death = $this->preFollowPatientAggregate($req, 2);

        $loss = $this->preFollowPatientAggregate($req, 3);

        $other = $this->preFollowPatientAggregate($req, 4);

        $cycle = $this->preFollowPatientAggregate($req, 5);

        $invalid = $this->preFollowPatientAggregate($req, 6);


//        $unique = array_merge_recursive($pending['data'], $death['data'], $loss['data'], $other['data'], $cycle['data'], $invalid['data']);
//
//        $total = array_unique($unique, SORT_REGULAR);

        return [
            'total' =>$totalCount,
            'pending' => $pending['num'],
            'death' => $death['num'],
            'loss' => $loss['num'],
            'other' => $other['num'],
            'cycle' => $cycle['num'],
            'invalid' => $invalid['num']
        ];
    }

    /**
     * 预随访患者初始条件
     * @param int $type 类型 1 待随访，2 已标记死亡患者，3 失访人数，4 其他任务中人数，5 周期内就诊或已随访人数，6 无效联系方式人数
     * @param string $cycle 随访周期日期
     * @return array
     */
    protected function preFollowPatientCondition(int $type, string $cycle=''): array
    {
        $match = [];
        switch ($type) {
            case 1:
                $overall = $this->getFollowOverall();
                $cycle = date("Y-m-d", strtotime('-'.$cycle.'day', time()));
                $search['$and'] = [
                    ['$or' => [
                        ['follow_node.follow_time' => ['$exists' => false]],
                        ['follow_node.follow_time' => ['$lte' => $cycle]]
                    ]],
                    ['$or' => [
                        ['calc_date_node.last_visit_time' => ['$exists' => false]],
                        ['calc_date_node.last_visit_time' => ['$lte' => $cycle]]
                    ]],
                    ['$or' => [
                        ['calc_date_node.last_out_hospital_date' => ['$exists' => false]],
                        ['calc_date_node.last_out_hospital_date' => ['$lte' => $cycle]]
                    ]]
                ];
                $search['establish_followtask_node.status'] = ['$nin' => [0, 1]];
                $search['lost_access'] = ['$ne' => 1];
                $match = array_merge_recursive($search, $overall);
                break;
            case 2:
                $match['liveStatus'] = '0';
                break;
            case 3:
                $match['lost_access'] = 1;
                break;
            case 4:
                $match['establish_followtask_node.status'] = ['$in' => [0, 1]];
                break;
            case 5:
                $cycle = date("Y-m-d", strtotime('-'.$cycle.'day', time()));
                $match['$or'] = [
                    ['follow_node.follow_time' => ['$gt' => $cycle]],
                    ['calc_date_node.last_visit_time' => ['$gt' => $cycle]],
                    ['calc_date_node.last_out_hospital_date' => ['$gt' => $cycle]],
                ];
                break;
            case 6:
                $match['$or'] = [
                    ['presentTelphone' => ['$exists' => false]],
                    ['presentTelphone' => ''],
                    ['presentTelphone' => null],
                    ['relationInfo.is_valid' => '2']
                ];
                break;
            default:
                throw new FailedException('类型不可为空');
        }
        $match['deleted_at'] = 0;
        return $match;
    }

    /**
     * 预随访患者统计数聚合方法
     * @return array
     */
    protected function preFollowPatientAggregate(array $req, int $type=1): array
    {
        if (!empty($req['request_id'])) {
            $rnd = Redis::getInstance()->redis->get("advSearch:" . $req['request_id']);
            $rndArr = json_decode($rnd, true, 512, JSON_THROW_ON_ERROR);
            $cond = $this->preFollowPatientCondition($type, $req['cycle']);
            $match = array_merge_recursive($rndArr, $cond);

        } else {
            //获取筛选条件
            $search = $this->getFollowScreenItem($req);

            //初始查询条件
            $cond = $this->preFollowPatientCondition($type, $req['cycle']);

            //获取配置条件
            $match = array_merge_recursive($search, $cond);
        }

        //如果设置抽取比例
        if (isset($req['ratio']) && !empty($req['ratio'])) {
            //验证收取比例参数是不是大于0小于100的正整数
            if ((int)$req['ratio'] < 0 || (int)$req['ratio'] > 100) {
                throw new FailedException('抽取比例参数必须是大于0和小于100的数');
            }
            //计算抽取比例
            $totalPip = [['$match' => $match], ['$count' => 'number']];
            $total = (new PatientModel())->mongoAggregate($totalPip);
            $number = 0;
            foreach ($total as $k => $v) {
                $number = $v["number"];
            }
            $ratio = $number * ((int)$req['ratio'] / 100);
            $ratio = round($ratio);
            $pipCount = [['$match' => $match], ['$sample' => ['size' => $ratio]],  ['$count' => 'total_count']];
            $pipData = [['$match' => $match], ['$sample' => ['size' => $ratio]],  ['$project' => ['_id' => false, 'patientId' => '$patientId']]];
        }else{
            $pipCount = [['$match' => $match], ['$count' => 'total_count']];
            $pipData = [['$match' => $match], ['$project' => ['_id' => false, 'patientId' => '$patientId']]];
        }
        //聚合总数
        $total = (new PatientModel())->mongoAggregate($pipCount);

        $totalCount = 0;
        foreach ($total as $key => $val) {
            $totalCount = $val["total_count"];
        }

        //聚合数据
        $data = (new PatientModel())->mongoAggregate($pipData);
        $retData = [];
        foreach ($data as $key => $val) {
            $retData[] = (array)$val;
        }
        return [
            'num' => $totalCount,
            'data' => $retData
        ];
    }

    /**
     * 随访计划导出SQL聚合条件
     * @param array $followIds 随访id(多个)
     * @return array
     */
    public function followTaskExportCondition(array $followIds = []): array
    {
        $pipLine =  [['$match' => ['_id' => ['$in' => $followIds]]], ['$lookup' => ['from' => 'patient_info', 'localField' => 'patientId', 'foreignField' => 'patientId', 'as' => 'info']], ['$unwind' => ['path' => '$info', 'preserveNullAndEmptyArrays' => true]], ['$lookup' => ['from' => 'follow_history', 'localField' => '_id', 'foreignField' => 'follow_id', 'as' => 'history']], ['$addFields' => ['latest_history' => ['$arrayElemAt' => [['$slice' => ['$history', -1]], 0]]]], ['$project' => ['history' => 0]], ['$project' => ['_id' => false, 'name' => '$name', 'medicalRecordsNo' => ['$ifNull' => ['$medicalRecordsNo', '']], 'inhospitalNo' => ['$ifNull' => ['$info.inList.inhospitalNo', '']], 'is_in_hospital' => ['$ifNull' => ['$info.is_in_hospital', '']], 'sex' => ['$ifNull' => ['$sex', '']], 'birthdate' => ['$ifNull' => ['$info.birthdate', '']], 'nation' => ['$ifNull' => ['$info.nation', '']], 'profession' => ['$ifNull' => ['$info.profession', '']], 'idno' => '$info.idno', 'presentTelphone' => ['$ifNull' => ['$presentTelphone', '']], 'diagnosis' => ['$ifNull' => ['$diagnosis', '']], 'liveStatus' => '$info.liveStatus', 'deathDate' => ['$ifNull' => ['$info.death.deathDate', '']], 'causeOfDeath' => ['$ifNull' => ['$info.death.causeOfDeath', '']], 'first_treat_age' => ['$ifNull' => ['$info.first_treat_age', '']], 'first_treat_datetime' => ['$ifNull' => ['$info.first_treat_datetime', '']], 'taskname' => '$taskname', 'follow_staff' => '$follow_staff.username', 'mode' => '$mode', 'followStatus' => '$followStatus', 'is_effective' => ['$ifNull' => ['$is_effective', '']], 'pushtime' => ['$ifNull' => ['$pushtime', '']], 'resultName' => ['$ifNull' => ['$resultName', '']], 'contact_person' => ['$ifNull' => ['$info.contactName', '']], 'contact_phone_no' => ['$ifNull' => ['$info.contactTelphone', '']], 'inhospitalDept' => ['$ifNull' => ['$info.inList.inhospitalDept', '']], 'inhospitalWard' => ['$ifNull' => ['$info.inList.inhospitalWard', '']], 'inchargeDoctorNo' => ['$ifNull' => ['$info.inList.inchargeDoctorNo', '']], 'inchargeDoctorName' => ['$ifNull' => ['$info.inList.inchargeDoctorName', '']], 'health_status' => ['$ifNull' => ['$latest_history.health_status', '']], 'adverse_reaction' => ['$ifNull' => ['$latest_history.adverse_reaction', '']], 'followup_conclusion' => ['$ifNull' => ['$latest_history.followup_conclusion', '']], 'recurrenceTime' => ['$ifNull' => ['$latest_history.recurrence.checkTime', '']], 'recurrencePlace' => ['$ifNull' => ['$latest_history.recurrence.place', '']], 'transferTime' => ['$ifNull' => ['$latest_history.transfer.checkTime', '']], 'transferPlace' => ['$ifNull' => ['$latest_history.transfer.place', '']]]]];

        $data = ZhyxMongo::getInstance()->setCollection('follow')->aggregate($pipLine);

        $ret = [];
        foreach ($data as $k => $v) {
            $v['is_in_hospital'] = $v['is_in_hospital'] == 1?"是":"否";
            $v['inhospitalNo'] = $v['inhospitalNo'].' ';
            $v['idno'] = $v['idno'].' ';
            $v['liveStatus'] = $v['liveStatus'] == '1'?"生存":"死亡";
            $v['followStatus'] = $v['followStatus'] == 1?"已随访":"未随访";
            $ret[] = (array)$v;
        }
        return $ret;
    }
}